package cn.acyou.utils;

import cn.acyou.actions.convertJson.MyToolWindowFactory;
import cn.acyou.exception.UnknownException;
import com.google.common.base.CaseFormat;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.PropertiesUtil;
import com.intellij.psi.PsiClassType;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.Yaml;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static cn.acyou.utils.PojoToJsonCore.GSON;

/**
 * @author youfang
 * @version [1.0.0, 2021/3/25]
 **/
public class CommonUtil {
    public static final List<String> MAPPING_LIST = new ArrayList<>();
    public static final List<String> application_dev = Arrays.asList("application-dev.properties", "application-dev.yml");
    public static final List<String> application = Arrays.asList("application.properties", "application.yml");
    public static final List<String> baseType = Arrays.asList("Long", "Integer", "String", "Boolean", "Double", "Float",
            "long", "int", "boolean", "double", "float");


    public static final String lineSeparator = System.getProperty("line.separator", "\n");


    static {
        MAPPING_LIST.add("RequestMapping");
        MAPPING_LIST.add("GetMapping");
        MAPPING_LIST.add("PostMapping");
        MAPPING_LIST.add("DeleteMapping");
        MAPPING_LIST.add("PutMapping");
    }

    public static String toStr(Object value, String defaultV) {
        return value != null ? value.toString() : defaultV;
    }

    public static String extractMapping(String text) {
        String path = "";
        if (text.indexOf("value") > 0) {
            int value = text.indexOf("\"", text.indexOf("value")) + 1;
            path = text.substring(value, text.indexOf("\"", value));
        } else {
            int value = text.indexOf("\"") + 1;
            path = text.substring(value, text.indexOf("\"", value));
        }
        if (path.startsWith("/")) {
            return path.substring(1);
        }
        return path;
    }

    public static void convertToJson(Project project, PsiClassType psiType) {
        if (psiType == null) {
            return;
        }
        String className = PojoToJsonCore.getClassName(psiType);

        ProgressManager.getInstance().run(new Task.Backgroundable(project, "转换 " + className + " 中...") {
            @Override
            public void run(@NotNull ProgressIndicator indicator) {
                // 10% done
                indicator.setFraction(0.1);
                indicator.setText("90% to finish");
                ProcessingInfo processingInfo = new ProcessingInfo().setProject(project).setProgressIndicator(indicator);
                try {
                    ApplicationManager.getApplication().runReadAction(new Runnable() {
                        @Override
                        public void run() {
                            Object result = PojoToJsonCore.resolveType(psiType, processingInfo);
                            // fix simpleType not support
                            processingInfo.setResultIfAbsent(result);
                        }
                    });
                } catch (ProcessCanceledException e) {
                    // ignore
                } finally {
                    // Finished
                    indicator.setFraction(1.0);
                    indicator.setText("finished");
                    indicator.cancel();
                }

                Object result = processingInfo.getResult();
                if (result == null) {
                    return;
                }

                String json = GSON.toJson(result);
                try {
                    json = PojoToJsonCore.myFormat(json);
                } catch (IOException ex) {
                    throw new UnknownException("Error :" + ex.getMessage());
                }
                StringSelection selection = new StringSelection(json);
                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                clipboard.setContents(selection, selection);

                String message = "转换 " + className + " 为 JSON 成功，已复制到剪切板！";
                Notification success = MessageUtil.notifyGroup.createNotification(message, NotificationType.INFORMATION);
                Notifications.Bus.notify(success, project);

                MyToolWindowFactory.setContent(json);
            }
        });
    }


    public static void writeFiles(File directory, List<String> contentList) throws Exception {
        //FileWriter fw = new FileWriter(directory);// this can't output utf-8!!!
        OutputStreamWriter out = new OutputStreamWriter( new FileOutputStream(directory), StandardCharsets.UTF_8);
        PrintWriter pw = new PrintWriter(out);
        for (String content : contentList) {
            pw.write(content);
        }
        pw.flush();
        pw.close();
    }

    public static void writeFiles(File directory, String content) throws Exception {
        OutputStreamWriter out = new OutputStreamWriter( new FileOutputStream(directory), StandardCharsets.UTF_8);
        PrintWriter pw = new PrintWriter(out, true);
        pw.write(content);
        pw.flush();
        pw.close();
    }

    /**
     * 下划线 转 大写类名
     *
     * @param filedName 字段名
     * @return 大写类名
     */
    public static String convertCamelCase(String filedName) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, filedName.toLowerCase());
    }

    /**
     * 大写类名 首字母小写
     *
     * @param filedName 字段名
     * @return 大写类名
     */
    public static String convertCamelCase2camelCase(String filedName) {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, filedName);
    }

    /**
     * 下划线 转 小写方法名
     *
     * @param filedName 字段名
     * @return 小写方法名
     */
    public static String convertcamelCase(String filedName) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, filedName);
    }


    public static String ymlToProperties(InputStream is) {
        Map<String, Object> load = new Yaml().load(is);
        StringBuilder sb = new StringBuilder();
        forBuilderYmlProperties("", sb, load);
        return sb.toString();
    }

    public static String ymlToProperties(String text) {
        Map<String, Object> load = new Yaml().load(text);
        StringBuilder sb = new StringBuilder();
        forBuilderYmlProperties("", sb, load);
        return sb.toString();
    }

    private static void forBuilderYmlProperties(String prefix, StringBuilder sb, Map<String, Object> load) {
        for (Map.Entry<String, Object> stringObjectEntry : load.entrySet()) {
            final String key = stringObjectEntry.getKey();
            final Object value = stringObjectEntry.getValue();
            String thisKey = key;
            if (!prefix.isEmpty()) {
                thisKey = prefix + "." + key;
            }
            if (value instanceof Map) {
                forBuilderYmlProperties(thisKey, sb, (Map<String, Object>) value);
            } else if (value instanceof List) {
                List<?> l = (List) value;
                for (int i = 0; i < l.size(); i++) {
                    sb.append(String.format("%s[%s]=%s" + CommonUtil.lineSeparator, thisKey, i, CommonUtil.toStr(l.get(i), "")));
                }
            } else {
                sb.append(String.format("%s=%s" + CommonUtil.lineSeparator, thisKey, CommonUtil.toStr(value, "")));
            }
        }
    }


    public static String propertiesToYml(Map<String, String> propertiesMap) {
        Map<String, Object> load = new LinkedHashMap<>();
        for (Map.Entry<String, String> stringStringEntry : propertiesMap.entrySet()) {
            String k = stringStringEntry.getKey();
            String v = stringStringEntry.getValue();
            forPutItems(k, v, load);
        }
        StringBuilder sb = new StringBuilder();
        forBuilderPropertiesYml("", sb, load);
        return sb.toString();
    }

    public static String propertiesToYml(InputStream inputStream) {
        Map<String, String> propertiesMap = new LinkedHashMap<>();
        try {
            propertiesMap = PropertiesUtil.loadProperties(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        } catch (Exception ex) {
            //ignore
        }
        return propertiesToYml(propertiesMap);
    }

    private static void forBuilderPropertiesYml(String indent, StringBuilder sb, Map<String, Object> load) {
        for (Map.Entry<String, Object> stringObjectEntry : load.entrySet()) {
            String key = stringObjectEntry.getKey();
            Object value = stringObjectEntry.getValue();
            if (value instanceof Map) {
                sb.append(String.format(indent + "%s: " + CommonUtil.lineSeparator, key));
                forBuilderPropertiesYml(indent + "  ", sb, (Map<String, Object>) value);
            } else if (value instanceof List) {
                List<?> l = (List) value;
                sb.append(indent + key + ": " + CommonUtil.lineSeparator);
                for (int i = 0; i < l.size(); i++) {
                    String s = CommonUtil.toStr(l.get(i), "");
                    if ("*".equalsIgnoreCase(s)) {
                        s = "'*'";
                    }
                    sb.append(String.format("%s- %s" + CommonUtil.lineSeparator, indent, s));
                }
            } else {
                String s = CommonUtil.toStr(value, "");
                if ("*".equalsIgnoreCase(s)) {
                    s = "'*'";
                }
                sb.append(String.format("%s%s: %s" + CommonUtil.lineSeparator, indent, key, s));
            }
        }
    }

    private static void forPutItems(String k, String v, Map<String, Object> load) {
        if (k.contains(".")) {
            String first = k.substring(0, k.indexOf("."));
            String last = k.substring(k.indexOf(".") + 1);
            if (load.containsKey(first)) {
                forPutItems(last, v, (Map<String, Object>) load.get(first));
            } else {
                Map<String, Object> sub = new LinkedHashMap<>();
                load.put(first, sub);
                forPutItems(last, v, sub);
            }
        } else {
            if (isListProperties(k)) {
                String listKey = k.substring(0, k.indexOf("["));
                if (load.containsKey(listKey)) {
                    List<String> o = (List<String>) load.get(listKey);
                    o.add(v);
                } else {
                    List<String> o = new ArrayList<>();
                    o.add(v);
                    load.put(listKey, o);
                }
            } else {
                load.put(k, v);
            }
        }
    }

    private static boolean isListProperties(String text) {
        Pattern compile = Pattern.compile("\\[\\d+]");
        Matcher matcher = compile.matcher(text);
        return matcher.find();
    }


}